package com.parleys.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.parleys.domain.Channel;
import com.parleys.domain.Presentation;
import com.parleys.domain.PresentationAsset;
import com.parleys.domain.Space;
import com.parleys.domain.Speaker;
import com.parleys.helper.ApplicationConstants;
import com.parleys.service.intf.DBAdapter;
import com.parleys.service.intf.ParleysService;
import com.parleys.service.intf.RestClient;

@Singleton
public class ParleysServiceImpl implements ParleysService {

	private static final String TYPE = "type";
	private static final String TARGET = "target";
	private static final String VALUE = "value";
	private static final String SEQUENCE = "sequence";
	private static final String CUEPOINT = "cuepoint";
	private static final String DURATION = "duration";
	private static final String MP3_URL = "MP3URL";
	private static final String FULLNAME = "fullname";
	private static final String BIO = "bio";
	private static final String SPEAKERS = "speakers";
	private static final String SUMMARY = "summary";
	private static final String TITLE = "title";
	private static final String MEDIA_BASE_URL = "mediaBaseURL";
	private static final String ASSET_BASE_URL = "assetBaseURL";
	private static final String THUMBNAIL = "thumbnail";
	private static final String DESCRIPTION = "description";
	private static final String NAME = "name";
	private static final String ID = "id";

	private static final String TAG = ParleysServiceImpl.class.getName();
	
	private DBAdapter dbAdapter;
	private RestClient restClient;

	private static final ArrayList<Space> ONLINE_SPACES_CACHE = new ArrayList<Space>();
	private static final HashMap<Integer, List<Channel>> ONLINE_CHANNELS_CACHE = new HashMap<Integer, List<Channel>>();
	private static final HashMap<Integer, List<Presentation>> ONLINE_PRESENTATIONS_CACHE = new HashMap<Integer, List<Presentation>>();
	private static final HashMap<Integer, List<PresentationAsset>> ONLINE_ASSETS_CACHE = new HashMap<Integer, List<PresentationAsset>>();

	@Inject
	public ParleysServiceImpl(final RestClient restClient, final DBAdapter dbAdapter) {
		this.restClient = restClient;
		this.dbAdapter = dbAdapter;
	}

	public synchronized List<Space> getSpaces(final boolean online) {
		if (online) {
			return getSpacesOnline();
		} else {
			return getSpacesOffline();
		}
	}

	public synchronized List<Channel> getChannelsForSpace(final Integer spaceId, final boolean online) {
		if (online) {
			return getChannelsForSpaceOnline(spaceId);
		} else {
			return getChannelsForSpaceOffline(spaceId);
		}
	}

	public synchronized List<Presentation> getPresentationsForChannel(final Integer channelId, final boolean online) {
		if (online) {
			return getPresentationsForChannelOnline(channelId);
		} else {
			return getPresentationsForChannelOffline(channelId);
		}
	}

	@Override
	public List<Presentation> getDownloadedPresentations() {
		dbAdapter.openForReading();
		List<Presentation> presentations = dbAdapter.getDownloadedPresentations();
		dbAdapter.close();
		
		return presentations;
	}
	
	@Override
	public List<Presentation> searchDownloadedPresentations(final String query) {
		dbAdapter.openForReading();
		List<Presentation> presentations = dbAdapter.searchPresentation(query);
		dbAdapter.close();
		
		return presentations;
	}

	public synchronized List<PresentationAsset> getPresentationAssets(final Integer presentationId, final boolean online) {
		if (online) {
			return getAssetsForPresentationOnline(presentationId);
		} else {
			return getAssetsForPresentationOffline(presentationId);
		}
	}

	private List<Space> getSpacesOnline() {
		if (ONLINE_SPACES_CACHE.isEmpty()) {

			List<JSONObject> objects = restClient.get(ApplicationConstants.SITE_PARLEYS_SERVER
					+ "rest/mobile/spaces.form?index=0&paging=100");
			for (JSONObject object : objects) {
				try {
					// concatenating an empty String to be sure the string instance 
					// is not backed by a 100k char array (the full JSON response from the server)
					Space parleysSpace = new Space();
					parleysSpace.setId((Integer) object.get(ID));
					parleysSpace.setName("" + object.get(NAME));
					parleysSpace.setDescription("" + object.get(DESCRIPTION));
					parleysSpace.setThumbnail("" + object.get(THUMBNAIL));
					parleysSpace.setAssetBaseURL("" + object.get(ASSET_BASE_URL));
					parleysSpace.setMediaBaseURL("" + object.get(MEDIA_BASE_URL));
					ONLINE_SPACES_CACHE.add(parleysSpace);
				} catch (JSONException e) {
					Log.e(TAG, "error while getting online spaces", e);
				}
			}
		}

		return ONLINE_SPACES_CACHE;
	}

	private List<Space> getSpacesOffline() {
		dbAdapter.openForReading();
		List<Space> spaces = dbAdapter.getAllSpaces();
		dbAdapter.close();
		return spaces;
	}

	private List<Channel> getChannelsForSpaceOnline(final Integer spaceId) {
		final List<Channel> cachedChannels = ONLINE_CHANNELS_CACHE.get(spaceId);
		if (cachedChannels == null) {
			ArrayList<Channel> items = new ArrayList<Channel>();

			List<JSONObject> objects = restClient.get(ApplicationConstants.SITE_PARLEYS_SERVER
					+ "rest/mobile/channels.form?id=" + spaceId);
			for (JSONObject object : objects) {
				try {
					Channel parleysChannel = new Channel();
					parleysChannel.setId((Integer) object.get(ID));
					parleysChannel.setName("" + object.get(NAME));
					parleysChannel.setDescription("" + object.get(DESCRIPTION));
					if (object.isNull(THUMBNAIL)) {
						parleysChannel.setThumbnail(null);
					} else {
						parleysChannel.setThumbnail("" + object.get(THUMBNAIL));
					}
					parleysChannel.setAssetBaseURL("" + object.get(ASSET_BASE_URL));
					parleysChannel.setMediaBaseURL("" + object.get(MEDIA_BASE_URL));
					items.add(parleysChannel);
				} catch (JSONException e) {
					Log.e(TAG, "error while getting online channels", e);
				}
			}

			ONLINE_CHANNELS_CACHE.put(spaceId, items);
			return items;

		} else {
			return cachedChannels;
		}
	}

	private List<Channel> getChannelsForSpaceOffline(final Integer spaceId) {
		dbAdapter.openForReading();
		List<Channel> channels = dbAdapter.getAllChannelsForSpace(spaceId);
		dbAdapter.close();
		return channels;
	}

	private List<Presentation> getPresentationsForChannelOnline(final Integer channelId) {
		final List<Presentation> cachedPresentations = ONLINE_PRESENTATIONS_CACHE.get(channelId);
		if (cachedPresentations == null) {
			ArrayList<Presentation> items = new ArrayList<Presentation>();

			StringBuilder builder = new StringBuilder(512);
			builder.append(ApplicationConstants.SITE_PARLEYS_SERVER);
			builder.append("rest/mobile/presentations.form?id=");
			builder.append(channelId);
			builder.append("&index=0&paging=100"); //paging isnt working atm

			List<JSONObject> objects = restClient.get(builder.toString());

			for (JSONObject object : objects) {
				try {
					Presentation presentation = new Presentation();
					presentation.setId((Integer) object.get(ID));
					presentation.setName("" + object.get(TITLE));
					presentation.setDescription("" + object.get(SUMMARY));
					presentation.setThumbnail("" + object.get(THUMBNAIL));
					presentation.setMp3("" + object.get(MP3_URL));
					// presentation.setAssetBaseURL((String) object.get("assetBaseURL"));
					// presentation.setMediaBaseURL((String) object.get("mediaBaseURL"));

					JSONArray JSONSpeakers = object.getJSONArray(SPEAKERS);
					ArrayList<Speaker> speakers = new ArrayList<Speaker>();

					for (int i = 0; i < JSONSpeakers.length(); i++) {
						JSONObject JSONSpeaker = (JSONObject) JSONSpeakers.get(i);
						Speaker speaker = new Speaker();
						speaker.setId(JSONSpeaker.getInt(ID));
						speaker.setBio(""+JSONSpeaker.getString(BIO));
						speaker.setFullname(""+JSONSpeaker.getString(FULLNAME));
						speakers.add(speaker);
					}

					presentation.setSpeakers(speakers);
					items.add(presentation);

				} catch (JSONException e) {
					Log.e(TAG, "error while getting online presentations", e);
				}
			}

			ONLINE_PRESENTATIONS_CACHE.put(channelId, items);

			return items;
		} else {
			return cachedPresentations;
		}

	}

	private List<Presentation> getPresentationsForChannelOffline(final Integer channelId) {
		dbAdapter.openForReading();
		List<Presentation> presentations = dbAdapter.getAllPresentationsForChannelId(channelId);
		dbAdapter.close();
		return presentations;
	}

	private List<PresentationAsset> getAssetsForPresentationOnline(final Integer presentationId) {
		List<PresentationAsset> cachedAssets = ONLINE_ASSETS_CACHE.get(presentationId);
		if (cachedAssets == null) {
			ArrayList<PresentationAsset> items = new ArrayList<PresentationAsset>();

			List<JSONObject> objects = restClient.get(ApplicationConstants.SITE_PARLEYS_SERVER
					+ "rest/mobile/assets.form?id=" + presentationId);

			for (JSONObject object : objects) {
				try {
					PresentationAsset asset = new PresentationAsset();

					asset.setId((Integer) object.get(ID));
					asset.setDuration((Double) object.getDouble(DURATION));
					asset.setCuepoint((Double) object.getDouble(CUEPOINT));
					asset.setSequence((Integer) object.getInt(SEQUENCE));
					asset.setName("" + object.get(NAME));
					if (object.isNull(VALUE)) {
						Log.d("getPresentationAssets", "Value id null for type: " + object.get(TYPE));
					} else {
						asset.setValue("" + object.get(VALUE));
					}
					asset.setTarget("" + object.get(TARGET));
					asset.setType("" + object.get(TYPE));

					items.add(asset);

				} catch (JSONException e) {
					Log.e(TAG, "error while getting online assets", e);
				}
			}

			ONLINE_ASSETS_CACHE.put(presentationId, items);

			return items;
		} else {
			return cachedAssets;
		}
	}

	private List<PresentationAsset> getAssetsForPresentationOffline(final Integer presentationId) {
		dbAdapter.openForReading();
		List<PresentationAsset> presentations = dbAdapter.getAllAssetsForPresentationId(presentationId);
		dbAdapter.close();
		return presentations;
	}
	
	@Override
	public Boolean isPresentationDownloaded(final Integer presentationId) {
		dbAdapter.openForReading();
		Boolean returnValue =  dbAdapter.isPresentationPresentInDb(presentationId);
		dbAdapter.close();
		return returnValue;
	}

}
